#pragma once

#include <iostream>
#include <string>
#include <queue>
#include "Mutex.hpp"
#include "Cond.hpp"
#include <pthread.h>

const int defaultcap = 5;
using namespace CondModule;
using namespace MutexModule; 

template<class T>
class BlockQueue
{
private:
    bool isFull() { return _q.size() >= _cap; }
    bool isEmpty() { return _q.empty(); }
public:
    BlockQueue(int cap = defaultcap)
        :_cap(cap)
        ,_csleep_num(0)
        ,_psleep_num(0)
    {
        // pthread_mutex_init(&_mutex, nullptr);
        // pthread_cond_init(&_full_cond, nullptr);
        // pthread_cond_init(&_empty_cond, nullptr);
    }

    void Equeue(const T& data)
    {
        //pthread_mutex_lock(&_mutex);
        LockGuard guard(_mutex);
        
        //队列为满，生产者挂起等待
        while(isFull())
        {
            _psleep_num++;
            std::cout << "producer hang up and wait" << std::endl;
            //pthread_cond_wait(&_full_cond, &_mutex);
            _full_cond.Wait(_mutex);
            _psleep_num--;
        }
        _q.push(data);

        if(_csleep_num > 0)
        {
            std::cout << "wake up consumer" << std::endl;
            //pthread_cond_signal(&_empty_cond);
            _empty_cond.Signal();
        }

        //pthread_mutex_unlock(&_mutex);

    }

    T Pop()
    {
        //pthread_mutex_lock(&_mutex);
        LockGuard guard(_mutex);
        //队列为空，消费者挂起等待
        while (isEmpty())
        {
            _csleep_num++;
            std::cout << "consumer hang up and wait" << std::endl;
            //pthread_cond_wait(&_empty_cond, &_mutex);
            _empty_cond.Wait(_mutex);
            _csleep_num--;
        }
        T data = _q.front();
        _q.pop();

        if(_psleep_num > 0)
        {
            std::cout << "wake up producer" << std::endl;
            //pthread_cond_signal(&_full_cond);
            _full_cond.Signal();
        }

        //pthread_mutex_unlock(&_mutex);
        return data;
    }


    ~BlockQueue()
    {
        // pthread_mutex_destroy(&_mutex);
        // pthread_cond_destroy(&_full_cond);
        // pthread_cond_destroy(&_empty_cond);
    }

private:
    std::queue<T> _q;
    int _cap;

    Mutex _mutex;
    Cond _full_cond;
    Cond _empty_cond;

    int _csleep_num;
    int _psleep_num;
    
};